import axios from 'axios';
import * as THREE from 'three';
import { lngLatToMercator, getTexture } from '../../../../three/index';
import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js';
import create3dBar from './3DBar';

export default class {
  seletMash: any;
  scene: THREE.Scene;
  box = new THREE.Box3();
  mesh = new THREE.Group();
  body = new THREE.Group();
  line: THREE.LineSegments;
  camera: THREE.PerspectiveCamera;
  raycaster = new THREE.Raycaster();
  material: THREE.MeshPhongMaterial;
  config: any = { depth: 10, height: 10.5 };
  clickHandlCallBack: Function;
  constructor(option, camera) {
    this.camera = camera;
    this.body.name = 'body';
    this.config = { ...this.config, ...option };
    window.addEventListener('click', this.clickHandl.bind(this));
    window.addEventListener('mousemove', this.mouseMove.bind(this));
  }
  move(x, y) {
    this.mesh.translateX(x);
    this.mesh.translateY(y);
  }
  clickHandl(e) {
    const { innerWidth, innerHeight } = window;
    const x = (e.x / innerWidth) * 2 - 1;
    const y = -(e.y / innerHeight) * 2 + 1;
    const mouse = new THREE.Vector2(x, y);
    this.raycaster.setFromCamera(mouse, this.camera);
    const intersects = this.raycaster.intersectObjects(this.body.children);
    if (intersects.length) {
      this.clickHandlCallBack(intersects[0].object.userData);
    }
  }
  setCallBack(fun) {
    this.clickHandlCallBack = fun;
  }
  mouseMove(e) {
    const { x, y } = e;
    const { innerWidth, innerHeight } = window;
    const mouse = new THREE.Vector2((x / innerWidth) * 2 - 1, -(y / innerHeight) * 2 + 1);
    this.raycaster.setFromCamera(mouse, this.camera);
    const intersects = this.raycaster.intersectObjects(this.body.children);
    if (this.seletMash) {
      this.body.remove(this.seletMash);
      this.seletMash.material.dispose();
      this.seletMash.geometry.dispose();
      this.seletMash = null;
    }
    if (intersects.length && intersects[0].object instanceof THREE.Mesh) {
      const mesh = intersects[0].object;
      const geometry = mesh.geometry;
      this.seletMash = new THREE.Mesh(
        geometry,
        new THREE.MeshBasicMaterial({ color: 0x00ff00, transparent: true, opacity: 0.2 })
      );
      this.seletMash.name = mesh.name;
      this.body.add(this.seletMash);
    }
  }
  async load() {
    await this.createMaterial();
    const res = await axios.get(this.config.url);
    this.createLine(res.data.features);
    res.data.features.forEach(feature => {
      this.convaterProvice(feature);
    });
    this.mesh.add(this.body, this.line);
  }
  convaterProvice(data) {
    const { coordinates } = data.geometry;
    const { name, center, centerid } = data.properties;
    const shapes = coordinates.map(i => i.map(j => this.createShape(j)));
    const geometry = this.createGeometry(shapes);
    const mesh = new THREE.Mesh(geometry, this.material);
    mesh.name = name;
    mesh.userData = { center, centerid, name };
    this.body.add(mesh);
  }
  async createMaterial() {
    const texture: any = await getTexture({
      url: this.config.map,
      colorSpace: THREE.SRGBColorSpace
    });
    const normalMap: any = await getTexture({ url: this.config.normal.map });
    const normalScale = this.config.normal.scale;
    this.material = new THREE.MeshPhongMaterial({ map: texture, normalMap, normalScale });
  }
  createGeometry(list) {
    const { max, min } = this.box;
    const { depth } = this.config;
    const geometryList = list.map(shape => {
      const uvList: any = [];
      const geometry = new THREE.ExtrudeGeometry(shape, { depth });
      const position = geometry.attributes.position.array;
      for (let i = 0; i < position.length; i += 3) {
        const x = (position[i] - min.x) / (max.x - min.x);
        const y = (position[i + 1] - min.y) / (max.y - min.y);
        uvList.push(x, y);
      }
      const uv = new THREE.BufferAttribute(new Float32Array(uvList), 2);
      geometry.attributes.uv = uv;
      return geometry;
    });
    return mergeGeometries(geometryList, true);
  }
  createPoints(list, z) {
    const points: any = [];
    for (let i = 0; i < list.length - 1; i++) {
      const a = lngLatToMercator(list[i]);
      const b = lngLatToMercator(list[i + 1]);
      points.push(new THREE.Vector3(a.x, a.y, z), new THREE.Vector3(b.x, b.y, z));
    }
    return points;
  }
  createShape(list) {
    const points: any = [];
    list.forEach(item => {
      const point = lngLatToMercator(item);
      points.push(new THREE.Vector2(point.x, point.y));
    });
    return new THREE.Shape(points);
  }
  createLine(features) {
    let points = [];
    const { height } = this.config;
    features.forEach(feature => {
      const { coordinates } = feature.geometry;
      return coordinates.forEach(arr => {
        arr.forEach(list => {
          points = points.concat(this.createPoints(list, height));
        });
      });
    });
    const material = new THREE.LineBasicMaterial({ color: 0xffffff });
    const geometry = new THREE.BufferGeometry().setFromPoints(points);
    this.line = new THREE.LineSegments(geometry, material);
    this.box.setFromObject(this.line);
  }
}
